/* linux/drivers/mtd/nand/s3c_nand.c
 *
 * Copyright (c) 2007 Samsung Electronics
 *
 * Samsung S3C NAND driver
 *
 * $Id: s3c_nand.c,v 1.1 2010/10/26 07:47:50 jensen Exp $
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Based on nand driver from Ben Dooks <ben@simtec.co.uk>
 * modified by scsuh. based on au1550nd.c
 *
 * Many functions for hardware ecc are implemented by jsgood.
 */

/* Simple H/W Table for Implementation of S3C nand driver
 * by scsuh
 * ------------------------------------------------------------------
 * |    En/Dis CE           |  required  |                          |
 * |    En/Dis ALE          |      X     | * nand controller does   |
 * |    En/Dis CLE          |      X     | * nand controller does   |
 * |    Wait/Ready          |  required  |                          |
 * |    Write Command       |  required  |                          |
 * |    Write Address       |  required  |                          |
 * |    Write Data          |  required  |                          |
 * |    Read Data           |  required  |                          |
 * |    WP on/off           |  required  | * board specific         |
 * |    AP Specific Init    |  required  |                          |
 * ------------------------------------------------------------------
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/jiffies.h>
#include <linux/sched.h>

#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>

#include <asm/io.h>

#include <plat/regs-nand.h>
#include <plat/nand.h>

struct s3c_nand_regs {
	unsigned long nfconf  ;
	unsigned long nfcont  ;
	unsigned long nfcmd   ;
	unsigned long nfaddr  ;
	unsigned long nfdata  ;
	unsigned long nfmeccdata0;
	unsigned long nfmeccdata1;
	unsigned long nfseccdata0;
	unsigned long nfsblk  ;
	unsigned long nfeblk  ;
	unsigned long nfstat  ;
	unsigned long nfestat0;
	unsigned long nfestat1;
	unsigned long nfmecc0 ;
	unsigned long nfmecc1 ;
	unsigned long nfsecc  ;
	unsigned long nfmlcbitpt  ;
	unsigned long nf8eccerr0  ;
	unsigned long nf8eccerr1  ;
	unsigned long nf8eccerr2  ;
	unsigned long nfm8ecc0 ;
	unsigned long nfm8ecc1 ;
	unsigned long nfm8ecc2 ;
	unsigned long nfm8ecc3 ;
	unsigned long nfmlc8bitpt0 ;
	unsigned long nfmlc8bitpt1 ;	
};


struct nand_chip *nand;

static struct mtd_info *s3c_mtd = NULL;
static struct s3c_nand_regs *s3c_nand_regs;

/*
 * Hardware specific access to control-lines function
 * Written by jsgood
 */
static void s3c_nand_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
	unsigned int cur;
	void __iomem *regs = s3c_nand.regs;

	if (ctrl & NAND_CTRL_CHANGE) {
		if (ctrl & NAND_NCE) {
			if (dat != NAND_CMD_NONE) {
				cur = readl(regs + S3C_NFCONT);
				cur &= ~S3C_NFCONT_nFCE0;
				writel(cur, regs + S3C_NFCONT);
			}
		} else {
			cur = readl(regs + S3C_NFCONT);
			cur |= S3C_NFCONT_nFCE0;
			writel(cur, regs + S3C_NFCONT);
		}
	}

	if (dat != NAND_CMD_NONE) {
		if (ctrl & NAND_CLE)
			writeb(dat, regs + S3C_NFCMMD);
		else if (ctrl & NAND_ALE)
			writeb(dat, regs + S3C_NFADDR);
	}
}

static void nand_select_chip(struct mtd_info *mtd, int chipnr)
{

	switch (chipnr) {
	case -1:
		chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
		break;
	case 0:
		
		break;

	default:
		BUG();
	}
}

/*
 * Function for checking device ready pin
 * Written by jsgood
 */
static int s3c_nand_device_ready(struct mtd_info *mtd)
{
	void __iomem *regs = s3c_nand.regs;
/* it's to check the RnB nand signal bit and return to device ready condition in nand_base.c */
	return ((readl(regs + S3C_NFSTAT) & S3C_NFSTAT_BUSY));
}


static int __init s3c_nand_init(void)
{
	printk("S3C NAND Driver, (c) 2008 Samsung Electronics\n");

	struct s3c2410_platform_nand *plat = pdev->dev.platform_data;
	struct s3c2410_nand_set *sets;
	struct resource *res;
	int err = 0;
	int ret = 0;
	int nr_sets;
	int i, j, size;

	/* allocate memory for MTD device structure and private data */
	s3c_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);

	if (!s3c_mtd) {
		return -ENOMEM;
	}

	/* Get pointer to private data */
	nand = (struct nand_chip *) (&s3c_mtd[1]);

	/* Link the private data with the MTD structure */
	s3c_mtd->owner = THIS_MODULE;
	s3c_mtd->priv = nand;

	nand->select_chip   = nand_select_chip;
	nand->IO_ADDR_R		= (char *)(s3c_nand.regs + S3C_NFDATA);
	nand->IO_ADDR_W		= (char *)(s3c_nand.regs + S3C_NFDATA);
	nand->cmd_ctrl		= s3c_nand_hwcontrol;
	nand->dev_ready     = s3c_nand_device_ready;
	nand->options		= 0;

	nand->ecc.mode = NAND_ECC_SOFT;
	printk("S3C NAND Driver is using software ECC.\n");

	if (nand_scan(s3c_mtd, 1)) {
		ret = -ENXIO;
	}

	/* Register the partitions */
//	add_mtd_partitions(s3c_mtd, sets->partitions, sets->nr_partitions);
}

static void __exit s3c_nand_exit(void)
{
	
}

module_init(s3c_nand_init);
module_exit(s3c_nand_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jinsung Yang <jsgood.yang@samsung.com>");
MODULE_DESCRIPTION("S3C MTD NAND driver");

